package OInt(OInt, toOInt, fromOInt, select) where

import Vector

--@ \subsubsection{OInt}
--@
--@ \index{OInt@\te{OInt} (type)|textbf}
--@ The {\mbox{\te{OInt n}}} type is a type that can store
--@ a number in the range \qbs{0..n-1}.
--@ The representation of a \qbs{OInt n} takes up $n$ bits, where
--@ exactly one bit is a set to one, and the others are zero, i.e., it is
--@ a ``one-hot'' decoded version of the number.
--@ The reason to use a \te{OInt} number is that the \te{select}
--@ operation is more efficient than for an ordinary number;
--@ the code generated for select takes advantage of the fact that
--@ only one of the bits may be set at a time.
--@
--@ \begin{libverbatim}
--@ typedef ...  OInt #(type n) ... ;
--@ \end{libverbatim}

data OInt n = O (Vector n Bool)
    deriving (Bits)

--@ Numeric literals can be used as usual (indicating which
--@ bit should be set).
--@ \begin{libverbatim}
--@ instance Literal #(OInt#(n));
--@ \end{libverbatim}
instance Literal (OInt n)
  where
    fromInteger i =
       let result :: OInt n
           result = O (map ((==) i) genList)
       in if (isStaticIndex i) then
            if (inLiteralRange result i) then
              result
            else error (integerToString i +
                        " is not a legal OInt#(" +
                        integerToString (valueOf n) + ").")
          else -- don't check non-static integers
               result
    inLiteralRange _ i = i >= 0 && i < (valueOf n)

--@ Values can be compared for equality.
--@ \begin{libverbatim}
--@ instance Eq #(OInt#(n));
--@ \end{libverbatim}
instance Eq (OInt n)
  where
    (==) (O xs) (O ys) =      or (zipWith (&&) xs ys)
    (/=) (O xs) (O ys) = not (or (zipWith (&&) xs ys))

instance (Log n k) => Ord (OInt n)
  where
    (<)  x y = fromOInt x <  fromOInt y
    (<=) x y = fromOInt x <= fromOInt y
    (>)  x y = fromOInt x >  fromOInt y
    (>=) x y = fromOInt x >= fromOInt y


instance (Log n k) => PrimIndex (OInt n) k
  where
    isStaticIndex  = compose isStaticIndex pack
    toStaticIndex  = compose toStaticIndex fromOInt
    toDynamicIndex = fromOInt

--@ There are upper and lower bounds.
--@ \begin{libverbatim}
--@ instance Bounded #(OInt#(n));
--@ \end{libverbatim}
instance Bounded (OInt n)
  where
    minBound = fromInteger 0
    maxBound = fromInteger (valueOf n - 1)

instance PrimSelectable (OInt n) Bool
  where
   primSelectFn pos (O xs) = primSelectFn pos xs

--@ An ordinary number can be converted to an \te{OInt}.
--@ An out-of-range number gives an unspecified result.
--@ \begin{libverbatim}
--@ function OInt#(n) toOInt(Bit#(k) k)
--@   provisos( Log#(n,k)) ;
--@ \end{libverbatim}
toOInt :: (Log n k ) => Bit k -> OInt n
toOInt k = O (map (\ i -> fromInteger i == k) genList)

--@ An \te{OInt} can be converted to an ordinary number.
--@ \begin{libverbatim}
--@ function Bit#(k) fromOInt(OInt#(n) o)
--@   provisos( Log#(n,k)) ;
--@ \end{libverbatim}
fromOInt :: (Log n k ) => OInt n -> Bit k
fromOInt o = select (map fromInteger genList) o

--@ An \te{OInt} can be used to select an element from a
--@ Vector in an efficient way.
--@ \begin{libverbatim}
--@ function a select(Vector#(n, a) xs, OInt#(n) o)
--@   provisos (Bits#(a, sa));
--@ \end{libverbatim}
select :: (Bits a sa) => Vector n a -> OInt n -> a
select xs (O bs) =
    let f x b =
            let vb :: Vector sa Bool
                vb = unpack (pack x)
            in  map ((&&) b) vb
    in  unpack (pack (map or (transpose (zipWith f xs bs))))

or :: Vector n Bool -> Bool
or = foldr (||) False
